package services

import (
	"bytes"
	"context"
	"fmt"
	"github.com/prometheus/common/model"
	"github.com/spf13/viper"
	"github.com/uber/tchannel-go"
	"github.com/uber/tchannel-go/thrift"
	"go.didapinche.com/foundation/plat-echo/app/ops/models"
	"go.didapinche.com/foundation/plat-echo/app/ops/repositories"
	"go.didapinche.com/foundation/plat-echo/pkg/constants"
	"go.didapinche.com/foundation/plat-echo/pkg/dingding"
	"go.didapinche.com/foundation/plat-echo/pkg/phone"
	promentheus "go.didapinche.com/foundation/plat-echo/pkg/prometheus"
	"go.didapinche.com/foundation/plat-echo/pkg/sms"
	"go.didapinche.com/foundation/plat-echo/pkg/utils/data"
	"go.didapinche.com/goapi/user_department_service_api"
	"go.didapinche.com/time"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"text/template"
	time2 "time"
)

const (
	AlertFiringTitle   = `告警 `
	AlertResolvedTitle = `恢复 `

	AlertFiringTmp = `
{{$owners := .owners}}
{{$upstreams := .upstreams}}
#### 告警 *{{ .name }}* 
---
{{ if ne .app "" }}##### 告警信息：*{{ .message }}* {{end}}
{{ if ne .app "" }}##### 应用：*{{ .app }}* {{end}}
{{ if ne .api "" }}##### 接口：*{{ .api }}* {{end}}
{{ if ne .host "" }}##### 主机：*{{ .host }}* {{end}}
{{ if ne .alert "" }}##### 告警级别：*{{ .alert }}* {{end}}
##### 阈值：*{{ .threshold }}*
##### Value：*{{ .value }}*
{{range $_,$owner := $owners}}[@{{$owner}}](){{end}}
> [查看详情]({{ .urlDetail }})  [认领]({{ .urlAlert }})
---
###### {{ .expression }}

{{ if ne .hasUp "" }}
---
###### 上游服务
{{range $_,$stream := $upstreams}}
{{$stream.Name}} {{range $_,$owner := $stream.Users}}[@{{$owner}}](){{end}}
{{end}}  
{{end}}`

	AlertResolvedTmp = `
{{$owners := .owners}}
{{$upstreams := .upstreams}}
#### 恢复 *{{ .name }}*
---
{{ if ne .app "" }}##### 告警信息：*{{ .message }}* {{end}}
{{ if ne .app "" }}##### 应用：*{{ .app }}* {{end}}
{{ if ne .api "" }}##### 接口：*{{ .api }}* {{end}}
{{ if ne .host "" }}##### 主机：*{{ .host }}* {{end}}
{{ if ne .alert "" }}##### 告警级别：*{{ .alert }}* {{end}}
##### 阈值：*{{ .threshold }}*
##### Value：*{{ .value }}*
{{range $_,$owner := $owners}}[@{{$owner}}](){{end}}
>  [查看详情]({{ .urlDetail }}) [认领]({{ .urlAlert }})
---
###### {{ .expression }}
{{ if ne .hasUp "" }}
---
###### 上游服务
{{range $_,$stream := $upstreams}}
{{$stream.Name}} {{range $_,$owner := $stream.Users}}[@{{$owner}}](){{end}}
{{end}}    
{{end}}`

	SmsTmp = `
{{$names := .names}}
{{$labels := .labels}}
告警：{{ .name }}
{{ if ne .app "" }}
应用：{{ .app }}
{{end}}
阈值：{{ .threshold }}
负责人：{{range $_,$name := $names}}{{$name}} {{end}}
- {{ .message }}
查看详情：{{ .urlDetail }}
`

	SmsApiTmp = `
{{$names := .names}}
{{$labels := .labels}}
告警：{{ .name }}
{{ if ne .app "" }}
应用：{{ .app }}

方法名称：{{ .method }}
接口级别：{{ .level }}
{{end}}
阈值：{{ .threshold }}
负责人：{{range $_,$name := $names}}{{$name}} {{end}}
- {{ .message }}
查看详情：{{ .urlDetail }}
`
)

type HookService interface {
	Dispatch(ctx context.Context, info promentheus.AlertInfo) error
}

type hookService struct {
	history            repositories.AlertHistoryRepository
	appRepository      repositories.ApplicationRepository
	templateRepository repositories.TemplateRepository
	graphRepository    repositories.GraphRepository
	dingding           dingding.DingDingService
	dingdingRepository repositories.DingdingRepository
	apiRepository      repositories.ApiRepository
	logger             *zap.Logger
	prome              *promentheus.PrometheusService
	userService        user_department_service_api.TChanUserService
	sms                *sms.SmsService
	alertRepository    repositories.AlertCurrentRepository
	phoneService       *phone.PhoneService
	mutex              sync.Mutex
}

func NewHookService(history repositories.AlertHistoryRepository,
	appRepository repositories.ApplicationRepository,
	dingding dingding.DingDingService,
	phoneService *phone.PhoneService,
	alertTrpository repositories.AlertCurrentRepository,
	apiRepository repositories.ApiRepository,
	prome *promentheus.PrometheusService,
	graphRepository repositories.GraphRepository,
	dingdingReposotory repositories.DingdingRepository,
	sms *sms.SmsService,
	templateRepository repositories.TemplateRepository,
	uic user_department_service_api.TChanUserService,
	logger *zap.Logger) HookService {
	return &hookService{
		history:            history,
		templateRepository: templateRepository,
		graphRepository:    graphRepository,
		prome:              prome,
		apiRepository:      apiRepository,
		userService:        uic,
		dingdingRepository: dingdingReposotory,
		dingding:           dingding,
		sms:                sms,
		phoneService:       phoneService,
		alertRepository:    alertTrpository,
		appRepository:      appRepository,
		logger:             logger.With(zap.String("type", "services.hookService")),
	}
}

// 收集处理报警消息
func (s hookService) Dispatch(ctx context.Context, infos promentheus.AlertInfo) error {
	defer func() {
		if r := recover(); r != nil {
			s.logger.Error("recover DeleteAlertResource")
			fmt.Println(r)
		}
	}()
	s.mutex.Lock()
	defer s.mutex.Unlock()

	// infos.Alerts虽然是数组，但其实是一个图到告警信息，可能因为host等标签不同被alertmanager使用group分组
	// 这里可以提前对infos.Alerts合并成一个数据，也可以只保存第一条或者最后一条，对所有消息进行钉钉告警
	// 目前处理策略是只保存一条但对所有进行钉钉告警
	for _, info := range infos.Alerts {
		history := new(models.AlertHistory)
		alert := new(models.AlertCurrent)
		application := new(models.Application)
		// kind可以区分app，api，dashoboard，host告警
		kind, err := strconv.Atoi(string(info.Labels["kind"]))
		if err != nil {
			s.logger.Error("kind don't int type", zap.Error(err))
			continue
		}
		history.Kind = kind
		// templateID可以查询出api对graph
		templateID, err := strconv.ParseUint(string(info.Labels["template_id"]), 10, 64)
		if err != nil {
			s.logger.Error("templateID don't int type", zap.Error(err))
			continue
		}
		history.TemplateID = templateID
		switch kind {
		case 0:
			GraphID, err := strconv.ParseUint(string(info.Labels["graph_id"]), 10, 64)
			if err != nil {
				s.logger.Error("graph_id don't int type", zap.Error(err))
				continue
			}
			history.GraphID = GraphID
			alert.GraphID = GraphID
			appID, err := strconv.ParseUint(string(info.Labels["app_id"]), 10, 64)
			if err != nil {
				s.logger.Error("appID don't int type", zap.Error(err))
				continue
			}
			alert.AppID = appID
			history.AppID = appID
			app, err := s.appRepository.FindByID(ctx, history.AppID)
			if err != nil {
				s.logger.Error("call appRepository FindByID", zap.Error(err))
				continue
			}
			application = app
			alert.Level = app.Level
			alert.AppID = app.ID
			alert.DevGroupID = app.DevGroupID
			history.AppID = app.ID
			history.Level = app.Level
			history.DevGroupName = app.DevGroupName
			history.DevGroupID = app.DevGroupID
		case 1:
			api := new(models.Api)
			if _, ok := info.Labels["uri"]; ok {
				uri := string(info.Labels["uri"])
				method := string(info.Labels["method"])
				app := string(info.Labels["app"])
				api, err = s.apiRepository.FindByHttpPath(ctx, app, method, uri)
				if err != nil {
					s.logger.Error("call apiRepository FindByID", zap.Error(err))
					continue
				}
			} else {
				service := string(info.Labels["service"])
				method := string(info.Labels["method"])
				app := string(info.Labels["app"])
				api, err = s.apiRepository.FindByZeus(ctx, app, service, method, 3)
				if err != nil {
					s.logger.Error("call apiRepository FindByID", zap.Error(err))
					continue
				}
			}
			graph, err := s.graphRepository.FindGraphByApiIDAndTemplateID(ctx, api.ID, templateID)
			if err != nil {
				s.logger.Error("call graphRepository FindGraphByApiIDAndTemplateID", zap.Error(err))
				continue
			}
			if graph.ID == 0 {
				continue
			}
			history.GraphID = graph.ID
			alert.GraphID = graph.ID
			alert.AppID = api.AppID
			alert.ApiID = api.ID
			alert.Level = api.Level
			alert.DevGroupID = api.DevGroupID
			history.AppID = api.AppID
			history.ApiID = api.ID
			history.Level = api.Level
			history.Type = api.Type
			history.DevGroupName = api.DevGroupName
			history.DevGroupID = api.DevGroupID

		case 2:
			break
		case 3:
			GraphID, err := strconv.ParseUint(string(info.Labels["graph_id"]), 10, 64)
			if err != nil {
				s.logger.Error("templateID don't int type", zap.Error(err))
				continue
			}
			history.GraphID = GraphID
			alert.GraphID = GraphID
			dashboardID, err := strconv.ParseUint(string(info.Labels["dashboard_id"]), 10, 64)
			if err != nil {
				s.logger.Error("appID don't int type", zap.Error(err))
				continue
			}
			alert.DashboardID = dashboardID
			history.DashboardID = dashboardID
		}
		graph, err := s.graphRepository.FindByID(ctx, history.GraphID)
		if err != nil {
			s.logger.Error("call graphRepository FindGraphByApiIDAndTemplateID", zap.Error(err))
			continue
		}
		// 比较一下阈值是否超过graph对阈值或者小于
		threshold, _ := data.ComputeThreshold(graph.Alert.Threshold)
		value, _ := strconv.ParseFloat(string(info.Annotations["value"]), 64)
		graph.Alert.Expression = strings.ReplaceAll(graph.Alert.Expression, "%s", "")
		graph.Alert.Expression = strings.ReplaceAll(graph.Alert.Expression, " ", "")
		si := ""
		if len(graph.Alert.Expression) > 0 {
			si = graph.Alert.Expression[len(graph.Alert.Expression)-1 : len(graph.Alert.Expression)]
		}
		if si == "<" {
			if threshold < value {
				continue
			}
		} else {
			if threshold >= value {
				continue
			}
		}
		alert.Alerts = new(models.Alerts)
		info.State = string(info.Alert.Status())
		info.StartAlertTime = time.Time{info.StartsAt.Local()}
		*alert.Alerts = append(*alert.Alerts, info)
		alert.StartAlertTime = time.Time{info.StartsAt.Local()}
		alert.Duration = time.Now().Sub(info.StartsAt).String()
		history.Alerts = new(models.Alerts)
		*history.Alerts = append(*history.Alerts, info)
		history.Message = string(info.Annotations["message"])
		history.AlertLevel = graph.Alert.AlertLevel
		alert.AlertLevel = graph.Alert.AlertLevel
		history.Owner = graph.Alert.GetOwner()
		alert.Owner = graph.Alert.GetOwner()
		history.Name = graph.Name
		if graph.Alert != nil {
			history.AlertName = graph.Name
			alert.AlertName = graph.Name
			history.Threshold = graph.Alert.Threshold
			alert.Threshold = graph.Alert.Threshold
		}
		// 过滤golang，java报警
		if kind == 0 && graph.TypeID == constants.Java && application.Language != "java" {
			continue
		}
		if kind == 0 && graph.TypeID == constants.Go && application.Language != "go" {
			continue
		}

		history.StartTime = time.Time{info.StartsAt.Local()}
		history.EndTime = time.Time{info.EndsAt.Local()}
		history.GeneratorURL = info.GeneratorURL
		history.Value = info.Value
		history.Duration = (history.EndTime.UnixNano() - history.StartTime.UnixNano()) / 1e6
		history.Date = data.FormatDate(history.StartTime.Time.Local(), data.Format_yyyyMMddHH)
		// 判断是恢复还是开始告警
		if info.Alert.Status() != model.AlertFiring {
			err := s.history.DeleteByCreate(ctx, history.GraphID, history.StartTime)
			if err != nil {
				_, err := s.history.FindByGraphIDAndStart(ctx, history.GraphID, history.StartTime)
				if err != gorm.ErrRecordNotFound {
					s.logger.Info("跳过一个重复的消息:" + strconv.Itoa(int(history.GraphID)))
					continue
				}
			}

			currents, err := s.alertRepository.FindByGraphID(ctx, history.GraphID, history.StartTime, history.EndTime)

			if err == nil {
				for _, c := range currents {
					if c.ClaimStatus == 1 {
						history.ClaimStatus = c.ClaimStatus
						history.ClaimUser = c.ClaimUser
						break
					}
				}
			}
			if err := s.history.Create(ctx, history); err != nil {
				s.logger.Error("call history.create error ", zap.Error(err))
				continue
			}
			if err := s.alertRepository.Delete(ctx, history.GraphID); err != nil {
				s.logger.Error("call alertRepository.Delete error ", zap.Error(err))
				continue
			}
			// 发送告警
			s.SendAlert(ctx, info)
		} else {
			alert.Name = info.Name()
			if err := s.alertRepository.Delete(ctx, history.GraphID); err != nil {
				s.logger.Error("call alertRepository.Delete error ", zap.Error(err))
				continue
			}
			if err := s.alertRepository.Create(ctx, alert); err != nil {
				s.logger.Error("call alertRepository.Create error ", zap.Error(err))
				continue
			}
			// 发送告警
			s.SendAlert(ctx, info)
		}
	}
	//s.SendAlerts(context.Background(), infos.Alerts)
	return nil
}

func (s hookService) SendAlert(ctx context.Context, info *promentheus.Alert) {
	defer func() {
		if r := recover(); r != nil {
			s.logger.Error("recover DeleteAlertResource")
			fmt.Println(r)
		}
	}()
	if !viper.GetBool("alert.switch") {
		return
	}
	kind, err := strconv.Atoi(string(info.Labels["kind"]))
	if err != nil {
		s.logger.Error("kind don't int type", zap.Error(err))
		return
	}
	s.SendDingding(context.Background(), info)
	if kind == 0 && info.Alert.Status() == model.AlertFiring {
		s.SendApp(context.Background(), info)
	} else if kind == 1 && info.Alert.Status() == model.AlertFiring {
		s.SendApi(context.Background(), info)
	}

}

func (s hookService) SendAlerts(ctx context.Context, infos []*promentheus.Alert) {
	defer func() {
		if r := recover(); r != nil {
			s.logger.Error("recover DeleteAlertResource")
			fmt.Println(r)
		}
	}()
	if !viper.GetBool("alert.switch") {
		return
	}
	for _, info := range infos {
		kind, err := strconv.Atoi(string(info.Labels["kind"]))
		if err != nil {
			s.logger.Error("kind don't int type", zap.Error(err))
			continue
		}
		s.SendDingding(context.Background(), info)
		if kind == 0 && info.Alert.Status() == model.AlertFiring {
			s.SendApp(context.Background(), info)
		} else if kind == 1 && info.Alert.Status() == model.AlertFiring {
			s.SendApi(context.Background(), info)
		}
	}
}

func (s hookService) SendApp(ctx context.Context, info *promentheus.Alert) error {

	defer func() {
		if r := recover(); r != nil {
			s.logger.Error("recover DeleteAlertResource")
			fmt.Println(r)
		}
	}()
	appID, err := strconv.ParseUint(string(info.Labels["app_id"]), 10, 64)
	if err != nil {
		s.logger.Error("appID don't int type", zap.Error(err))
		return err
	}
	graphID, err := strconv.ParseUint(string(info.Labels["graph_id"]), 10, 64)
	if err != nil {
		s.logger.Error("graphID don't int type", zap.Error(err))
		return err
	}

	app, err := s.appRepository.FindByID(ctx, appID)
	if err != nil {
		s.logger.Error("call appRepository.FindByID error", zap.Error(err))
		return err
	}
	owner := make([]string, 0)
	for _, o := range app.GetOwners() {
		owner = append(owner, o.UserID)
	}

	graph, err := s.graphRepository.FindByID(ctx, graphID)
	if err != nil {
		s.logger.Error("call graphRepository.FindByID error", zap.Error(err))
		return err
	}
	if graph.Alert.AlertLevel == 2 {
		return nil
	}
	if graph.Silence != nil {
		if time.Now().UnixNano() < graph.Silence.EndTime.UnixNano() {
			return nil
		}
	}
	m := make(map[string]interface{})
	m["app"] = app.Name
	m["host"] = ""
	label := make(map[string]string)
	for k, v := range info.Labels {
		banLabels := strings.Split(viper.GetString("alert.banLabels"), ",")
		if !data.ContainString(banLabels, string(k)) {
			label[string(k)] = string(v)
		}
	}

	if string(info.Labels[model.LabelName("hostname")]) == "" && string(info.Labels[model.LabelName("host")]) != "" {
		m["host"] = string(info.Labels[model.LabelName("host")])
	} else if string(info.Labels[model.LabelName("hostname")]) != "" && string(info.Labels[model.LabelName("host")]) == "" {
		m["host"] = string(info.Labels[model.LabelName("hostname")])
	} else if string(info.Labels[model.LabelName("hostname")]) != "" && string(info.Labels[model.LabelName("host")]) != "" {
		m["host"] = string(info.Labels[model.LabelName("hostname")]) + ":" + string(info.Labels[model.LabelName("host")])
	}

	m["labels"] = label
	phones := make([]string, 0)
	sms := make([]string, 0)
	if graph.Alert != nil {
		m["threshold"] = graph.Alert.Threshold
		m["message"] = graph.Alert.Message
		m["template_id"] = graph.TemplateId
	}
	if graph.Subscribe != nil {
		if data.ContainInt(graph.Subscribe.SubscribeForce, 0) {
			phones = append(phones, owner...)
		}
		if data.ContainInt(graph.Subscribe.SubscribeForce, 1) {
			sms = append(phones, owner...)
		}
		for _, p := range graph.Subscribe.Phones {
			phones = append(phones, p)
		}
		for _, s := range graph.Subscribe.Sms {
			sms = append(sms, s)
		}
	}
	m["level"] = app.Level
	//需要和前端配合
	urlDetail := "https://pass.didapinche.com/echo/v2/full/graph/" + strconv.Itoa(int(app.ID)) + "/" + app.Name + "?"
	urlDetail += "start_time=" + url.QueryEscape(time.Time{info.StartsAt.Local().Add(-1 * time2.Second)}.String()) + "&"
	urlDetail += "end_time=" + url.QueryEscape(time.Time{time.Now().Local()}.String())
	urlDetail += "&step=" + strconv.Itoa(int(time.Now().Time.Unix()-info.StartsAt.Unix())/100)
	m["urlDetail"] = urlDetail
	s.sms.Send(ctx, sms, SmsApiTmp, m)
	s.phoneService.Sends(phones)
	return nil
}

func (s hookService) SendApi(ctx context.Context, info *promentheus.Alert) error {
	defer func() {
		if r := recover(); r != nil {
			s.logger.Error("recover DeleteAlertResource")
			fmt.Println(r)
		}
	}()
	var err error
	templateID, err := strconv.ParseUint(string(info.Labels["template_id"]), 10, 64)
	if err != nil {
		s.logger.Error("templateID don't int type", zap.Error(err))
		return err
	}
	api := new(models.Api)
	if _, ok := info.Labels["uri"]; ok {
		uri := string(info.Labels["uri"])
		method := string(info.Labels["method"])
		app := string(info.Labels["app"])
		api, err = s.apiRepository.FindByHttpPath(ctx, app, method, uri)
		if err != nil {
			s.logger.Error("call apiRepository FindByID", zap.Error(err))
			return err
		}
	} else {
		service := string(info.Labels["service"])
		method := string(info.Labels["method"])
		app := string(info.Labels["app"])

		api, err = s.apiRepository.FindByZeus(ctx, app, service, method, 3)
		if err != nil {
			s.logger.Error("call apiRepository FindByID", zap.Error(err))
			return err
		}
	}
	graph, err := s.graphRepository.FindGraphByApiIDAndTemplateID(ctx, api.ID, templateID)
	if err != nil {
		s.logger.Error("call graphRepository FindGraphByApiIDAndTemplateID", zap.Error(err))
		return err
	}
	if graph.Alert.AlertLevel == 2 {
		return nil
	}
	threshold, _ := strconv.ParseFloat(graph.Alert.Threshold, 64)
	value, err := strconv.ParseFloat(string(info.Annotations["value"]), 64)
	graph.Alert.Expression = strings.ReplaceAll(graph.Alert.Expression, "%s", "")
	graph.Alert.Expression = strings.ReplaceAll(graph.Alert.Expression, " ", "")
	si := ""
	if len(graph.Alert.Expression) > 0 {
		si = graph.Alert.Expression[len(graph.Alert.Expression)-1 : len(graph.Alert.Expression)]
	}
	if si == "<" {
		if threshold < value {
			return nil
		}
	} else {
		if threshold >= value {
			return nil
		}
	}
	owner := make([]string, 0)
	for _, o := range api.GetOwners() {
		owner = append(owner, o.UserID)
	}
	if graph.Silence != nil {
		if time.Now().UnixNano() < graph.Silence.EndTime.UnixNano() {
			return nil
		}
	}
	m := make(map[string]interface{})
	m["host"] = ""
	label := make(map[string]string)
	for k, v := range info.Labels {
		banLabels := strings.Split(viper.GetString("alert.banLabels"), ",")
		if !data.ContainString(banLabels, string(k)) {
			label[string(k)] = string(v)
		}
	}
	if string(info.Labels[model.LabelName("hostname")]) == "" && string(info.Labels[model.LabelName("host")]) != "" {
		m["host"] = string(info.Labels[model.LabelName("host")])
	} else if string(info.Labels[model.LabelName("hostname")]) != "" && string(info.Labels[model.LabelName("host")]) == "" {
		m["host"] = string(info.Labels[model.LabelName("hostname")])
	} else if string(info.Labels[model.LabelName("hostname")]) != "" && string(info.Labels[model.LabelName("host")]) != "" {
		m["host"] = string(info.Labels[model.LabelName("hostname")]) + ":" + string(info.Labels[model.LabelName("host")])
	}
	m["labels"] = label
	m["app"] = api.AppName
	if api.Type == 0 {
		m["method"] = api.HttpMethod
		m["template_type"] = "http"
	} else {
		m["method"] = api.ZeusMethod
		m["template_type"] = "zeus"
	}
	phones := make([]string, 0)
	sms := make([]string, 0)
	if graph.Alert != nil {
		m["threshold"] = graph.Alert.Threshold
		m["message"] = graph.Alert.Message
		m["template_id"] = graph.TemplateId
	}
	if graph.Subscribe != nil {
		if data.ContainInt(graph.Subscribe.SubscribeForce, 0) {
			phones = append(phones, owner...)
		}
		if data.ContainInt(graph.Subscribe.SubscribeForce, 1) {
			sms = append(phones, owner...)
		}
		for _, p := range graph.Subscribe.Phones {
			phones = append(phones, p)
		}
		for _, s := range graph.Subscribe.Sms {
			sms = append(sms, s)
		}
	}
	//需要和前端配合
	urlDetail := viper.GetString("alert.url")
	urlDetail += "?start_time=" + url.QueryEscape(time.Time{info.StartsAt.Local().Add(-1 * time2.Second)}.String()) + "&"
	urlDetail += "end_time=" + url.QueryEscape(time.Time{time.Now().Local()}.String())
	urlDetail += "&graphId=" + strconv.Itoa(int(graph.ID))
	urlDetail += "&time_type=custom&type=edit&sourceModule=interface"
	urlDetail += "&interfaceId=" + strconv.Itoa(int(api.ID))
	m["urlDetail"] = urlDetail
	s.sms.Send(ctx, sms, SmsApiTmp, m)
	s.phoneService.Sends(phones)
	return nil
}

func (s hookService) SendDingding(ctx context.Context, info *promentheus.Alert) error {
	var (
		tmp   string
		title string
		token string
		graph *models.Graph
	)
	s.logger.Debug("钉钉告警开始")
	if info.Alert.Status() == model.AlertFiring {
		// 告警
		title = AlertFiringTitle
		tmp = AlertFiringTmp
	} else {
		// 解决
		title = AlertResolvedTitle
		tmp = AlertResolvedTmp
	}
	//if viper.GetBool("alert.time.utc"){
	//	info.StartsAt = info.StartsAt.Add(time2.Hour*8)
	//	if !info.EndsAt.IsZero(){
	//		info.EndsAt = info.EndsAt.Add(time2.Hour*8)
	//	}
	//}

	s.logger.Debug("钉钉告警开始获取graphID")
	var graphID uint64
	t, err := template.New("temp").Parse(tmp)
	if err != nil {
		return err
	}
	owner := make([]string, 0)
	var buf bytes.Buffer
	temMap := map[string]interface{}{
		"api":         "",
		"app":         "",
		"message":     "",
		"host":        "",
		"hasUp":       "",
		"alert":       "",
		"value":       "",
		"expression":  "",
		"zeusAppName": "",
		"claim":       "",
		"upstreams":   "",
		"threshold":   "",
	}
	temMap["message"] = info.Annotations["message"]
	upsteamUsers := make([]string, 0)
	kind, err := strconv.Atoi(string(info.Labels["kind"]))
	if err != nil {
		s.logger.Error("kind don't int type", zap.Error(err))
		return err
	}

	if v, ok := info.Labels[model.LabelName("service")]; ok {
		temMap["zeusAppName"] = string(v)
	}
	//查询dingding token
	if kind == 0 {
		s.logger.Debug("钉钉告警开始获取app")
		graphID, err = strconv.ParseUint(string(info.Labels["graph_id"]), 10, 64)
		if err != nil {
			s.logger.Error("graphID don't int type", zap.Error(err))
			return err
		}
		graph, err = s.graphRepository.FindByID(ctx, graphID)
		if err != nil {
			s.logger.Error("call graphRepository.FindByID error", zap.Error(err))
			return err
		}
		if graph.Alert.AlertLevel == 2 {
			return nil
		}
		if graph.Alert.AlertLevel == 0 {
			temMap["alert"] = "严重"
		} else if graph.Alert.AlertLevel == 1 {
			temMap["alert"] = "警告"
		}
		appID, err := strconv.ParseUint(string(info.Labels["app_id"]), 10, 64)
		if err != nil {
			s.logger.Error("appID don't int type", zap.Error(err))
			return err
		}
		app, err := s.appRepository.FindByID(ctx, appID)
		if err != nil {
			s.logger.Error("call appRepository.FindByID error", zap.Error(err))
			return err
		}
		temMap["app"] = app.Name
		temMap["api"] = ""
		urlDetail := viper.GetString("alert.url")
		urlDetail += "?start_time=" + url.QueryEscape(time.Time{info.StartsAt.Local()}.String()) + "&"
		urlDetail += "end_time=" + url.QueryEscape(time.Time{time.Now().Local()}.String())
		urlDetail += "&graphId=" + strconv.Itoa(int(graph.ID))
		urlDetail += "&time_type=custom&type=edit&sourceModule=app"
		urlDetail += "&appId=" + strconv.Itoa(int(app.ID))
		temMap["urlDetail"] = urlDetail
		temMap["urlAlert"] = urlDetail + "&auto_claim=true"

	} else if kind == 1 {
		templateID, err := strconv.ParseUint(string(info.Labels["template_id"]), 10, 64)
		if err != nil {
			s.logger.Error("templateID don't int type", zap.Error(err))
			return err
		}
		s.logger.Debug("钉钉告警开始获取api")
		api := new(models.Api)
		if _, ok := info.Labels["uri"]; ok {
			uri := string(info.Labels["uri"])
			method := string(info.Labels["method"])
			app := string(info.Labels["app"])
			api, err = s.apiRepository.FindByHttpPath(ctx, app, method, uri)
			if err != nil {
				s.logger.Error("call apiRepository FindByID", zap.Error(err))
				return err
			}
		} else {
			service := string(info.Labels["service"])
			method := string(info.Labels["method"])
			app := string(info.Labels["app"])
			api, err = s.apiRepository.FindByZeus(ctx, app, service, method, 3)
			if err != nil {
				s.logger.Error("call apiRepository FindByID", zap.Error(err))
				return err
			}
		}
		graph, err = s.graphRepository.FindGraphByApiIDAndTemplateID(ctx, api.ID, templateID)
		if err != nil {
			s.logger.Error("call graphRepository FindGraphByApiIDAndTemplateID", zap.Error(err))
			return err
		}
		graphID = graph.ID
		if graph.Alert.AlertLevel == 2 {
			return nil
		}
		if graph.Alert.AlertLevel == 0 {
			temMap["alert"] = "严重"
		} else if graph.Alert.AlertLevel == 1 {
			temMap["alert"] = "警告"
		}
		temMap["app"] = api.AppName
		urlDetail := viper.GetString("alert.url")
		urlDetail += "?start_time=" + url.QueryEscape(time.Time{info.StartsAt.Local()}.String()) + "&"
		urlDetail += "end_time=" + url.QueryEscape(time.Time{time.Now().Local()}.String())
		urlDetail += "&graphId=" + strconv.Itoa(int(graph.ID))
		urlDetail += "&time_type=custom&type=edit&sourceModule=interface"
		urlDetail += "&appId=" + strconv.Itoa(int(api.AppID))
		temMap["urlDetail"] = urlDetail
		temMap["urlAlert"] = urlDetail + "&auto_claim=true"
		if api.Type == 0 {
			temMap["api"] = api.HttpMethod + ":" + api.HttpPath
		} else {
			temMap["api"] = api.ZeusService + ":" + api.ZeusMethod
			appNames, _ := s.prome.QueryUpstream(api.ZeusMethod, api.ZeusService, time2.Now())
			if graph.TypeID == constants.ZeusClientTempType {
				temMap["hasUp"] = ""
			} else if len(appNames) > 0 {
				upstreams := make([]*models.Application, 0)
				for _, app := range appNames {
					up, err := s.appRepository.FindByName(ctx, app)
					if err == nil {
						upApp := s.GetUsers(up)
						if upApp != nil {
							temMap["hasUp"] = "1"
							upsteamUsers = append(upsteamUsers, upApp.Users...)
							upstreams = append(upstreams, upApp)
						}
					}
				}
				upstreamsMap := make(map[string]*models.Application)
				for _, u := range upstreams {
					upstreamsMap[u.Name] = u
				}
				upstreams2 := make([]*models.Application, 0)
				for _, v := range upstreamsMap {
					upstreams2 = append(upstreams2, v)
				}
				temMap["upstreams"] = upstreams2
			}
		}
	} else if kind == 3 {
		s.logger.Debug("钉钉告警开始获取dashboard")
		graphID, err = strconv.ParseUint(string(info.Labels["graph_id"]), 10, 64)
		if err != nil {
			s.logger.Error("graphID don't int type", zap.Error(err))
			return err
		}
		graph, err = s.graphRepository.FindByID(ctx, graphID)
		if err != nil {
			s.logger.Error("call graphRepository.FindByID error", zap.Error(err))
			return err
		}
		if graph.Alert.AlertLevel == 2 {
			return nil
		}
		if graph.Alert.AlertLevel == 0 {
			temMap["alert"] = "严重"
		} else if graph.Alert.AlertLevel == 1 {
			temMap["alert"] = "警告"
		}
		urlDetail := viper.GetString("alert.url")
		urlDetail += "?start_time=" + url.QueryEscape(time.Time{info.StartsAt.Local()}.String()) + "&"
		urlDetail += "end_time=" + url.QueryEscape(time.Time{time.Now().Local()}.String())
		urlDetail += "&graphId=" + strconv.Itoa(int(graph.ID))
		urlDetail += "&time_type=custom&type=edit&sourceModule=dashboard"
		temMap["urlDetail"] = urlDetail
		temMap["urlAlert"] = urlDetail + "&auto_claim=true"
	}
	if string(info.Labels[model.LabelName("hostname")]) == "" && string(info.Labels[model.LabelName("host")]) != "" {
		temMap["host"] = string(info.Labels[model.LabelName("host")])
	} else if string(info.Labels[model.LabelName("hostname")]) != "" && string(info.Labels[model.LabelName("host")]) == "" {
		temMap["host"] = string(info.Labels[model.LabelName("hostname")])
	} else if string(info.Labels[model.LabelName("hostname")]) != "" && string(info.Labels[model.LabelName("host")]) != "" {
		temMap["host"] = string(info.Labels[model.LabelName("hostname")]) + ":" + string(info.Labels[model.LabelName("host")])
	}
	if graph.Silence != nil {
		if time.Now().UnixNano() < graph.Silence.EndTime.UnixNano() {
			return nil
		}
	}
	if graph.Alert.ValidateAlert() {
		title += graph.Name
		temMap["name"] = graph.Name
		temMap["threshold"] = graph.Alert.Threshold
		expression := graph.Alert.Expression + graph.Alert.Threshold
		if len(expression) > 200 {
			expression = expression[0:200] + "..."
		}
		temMap["expression"] = expression
		temMap["message"] = graph.Alert.Message
		temMap["graph_id"] = graph.ID
		if graph.Alert.Receiver != nil {
			if len(graph.Alert.Receiver.Dingdings) > 0 {
				token = graph.Alert.Receiver.Dingdings[0].Token
			}
			for _, u := range graph.Alert.Receiver.Users {
				context2, _ := context.WithTimeout(ctx, 5*time2.Second)
				ctx2 := thrift.Wrap(context2)
				owner2, err := s.userService.FindByUid(ctx2, u.ID)
				if err == nil {
					owner = append(owner, owner2.Phone)
				}
			}
		}
	} else {
		return nil
	}

	threshold, _ := data.ComputeThreshold(graph.Alert.Threshold)
	value, _ := strconv.ParseFloat(string(info.Annotations["value"]), 64)
	graph.Alert.Expression = strings.ReplaceAll(graph.Alert.Expression, "%s", "")
	graph.Alert.Expression = strings.ReplaceAll(graph.Alert.Expression, " ", "")
	si := ""
	if len(graph.Alert.Expression) > 0 {
		si = graph.Alert.Expression[len(graph.Alert.Expression)-1 : len(graph.Alert.Expression)]
	}
	if si == "<" {
		if threshold < value {
			return nil
		}
	} else {
		if threshold >= value {
			return nil
		}
	}

	temMap["owners"] = owner
	var format models.UnitType
	if graph.General != nil {
		format = graph.General.Yaxes.Format
	}
	temMap["value"] = models.FormatValue(string(info.Annotations["value"]), format)
	s.logger.Debug("钉钉告警开始执行模版转化")
	err = t.Execute(&buf, temMap)
	if err != nil {
		s.logger.Error("execute template error", zap.Error(err))
		return err
	}
	msg := &dingding.DingMessage{
		MessageType: "markdown",
		Markdown: dingding.Markdown{
			Title: title,
			Text:  buf.String(),
		},
		At: dingding.At{
			IsAtAll: false,
		},
	}
	msg.At.AtMobiles = append(owner, upsteamUsers...)
	s.logger.Debug("钉钉告警开始发送")
	token = strings.Trim(token, " ")
	err = s.dingding.SendMessage(token, msg)
	if err != nil {
		s.logger.Error("钉钉告警失败发送", zap.Error(err))
	} else {
		s.logger.Debug("钉钉告警发送完成")
	}
	return err
}

func (h hookService) GetUsers(app *models.Application) *models.Application {
	owner := app.GetOwners()
	for _, user := range owner {
		ctx, _ := tchannel.NewContextBuilder(time2.Second).Build()
		owner, err := h.userService.FindByUid(ctx, user.UserID)
		if err == nil {
			app.Users = append(app.Users, owner.Phone)
		}
	}
	return app
}
